home *** CD-ROM | disk | FTP | other *** search
/ Aminet 1 (Walnut Creek) / Aminet - June 1993 [Walnut Creek].iso / usenet / sources / volume89 / comm / amigatcp.4 < prev    next >
Text File  |  1989-03-18  |  52KB  |  2,360 lines

  1. Path: xanth!ukma!tut.cis.ohio-state.edu!mailrus!ulowell!page
  2. From: page@swan.ulowell.edu (Bob Page)
  3. Newsgroups: comp.sources.amiga
  4. Subject: v89i083:  amigatcp - tcp/ip for the amiga, Part04/06
  5. Message-ID: <12331@swan.ulowell.edu>
  6. Date: 17 Mar 89 23:18:59 GMT
  7. Organization: University of Lowell, Computer Science Dept.
  8. Lines: 2349
  9. Approved: page@swan.ulowell.edu
  10.  
  11. Submitted-by: rminnich@super.org (Ronald G. Minnich)
  12. Posting-number: Volume 89, Issue 83
  13. Archive-name: comm/amigatcp.4
  14.  
  15. #    This is a shell archive.
  16. #    Remove everything above and including the cut line.
  17. #    Then run the rest of the file through sh.
  18. #----cut here-----cut here-----cut here-----cut here----#
  19. #!/bin/sh
  20. # shar:    Shell Archiver
  21. #    Run the following text with /bin/sh to create:
  22. #    arp.c
  23. #    ax25.c
  24. #    ftpcli.c
  25. #    smtpserv.c
  26. # This archive created: Fri Mar 17 18:01:19 1989
  27. cat << \SHAR_EOF > arp.c
  28. /* Address Resolution Protocol (ARP) functions. Sits between IP and
  29.  * Level 2, mapping IP to Level 2 addresses for all outgoing datagrams.
  30.  */
  31. #include "machdep.h"
  32. #include "mbuf.h"
  33. #include "timer.h"
  34. #include "iface.h"
  35. #include "ether.h"
  36. #include "ax25.h"
  37. #include "arp.h"
  38. #include "cmdparse.h"
  39.  
  40. extern int32 ip_addr;        /* Our IP address */
  41.  
  42. int ec_output();
  43. int pether(),gether();
  44.  
  45. int setcall(),psax25();
  46.  
  47. /* Table of ARP hardware types */
  48. struct arp_type arp_type[] = {
  49.     0,
  50.     0,
  51.     0,
  52.     NULLCHAR,
  53.     NULLFP,
  54.     NULLFP,
  55.  
  56.     /* 10 megabit Ethernet */
  57.     6,            /* Ethernet address length */
  58.     0x800,            /* Ethernet type field for IP */
  59.     0x806,            /* Ethernet type field for ARP */
  60.     ether_bdcst,        /* Ethernet broadcast address */
  61.     pether,
  62.     gether,
  63.     
  64.     /* 3 megabit Ethernet */
  65.     0,
  66.     0,
  67.     0,
  68.     NULLCHAR,
  69.     NULLFP,
  70.     NULLFP,
  71.  
  72.     /* AX.25 */
  73.     7,            /* AX.25 address length */
  74.     0xCC,            /* AX.25 pid field for IP */
  75.     0xCD,            /* AX.25 pid field for ARP */
  76.     (char *)&ax25_bdcst,    /* AX.25 broadcast address */
  77.     psax25,
  78.     setcall,
  79. };
  80. #define    NTYPES    4
  81.  
  82. /* Hash table headers */
  83. struct arp_tab *arp_tab[ARPSIZE];
  84.  
  85. struct arp_stat arp_stat;
  86.  
  87. /* Resolve an IP address to a hardware address; if not found,
  88.  * initiate query and return NULLCHAR.  If an address is returned, the
  89.  * interface driver may send the packet; if NULLCHAR is returned,
  90.  * res_arp() will have saved the packet on its pending queue,
  91.  * so no further action (like freeing the packet) is necessary.
  92.  */
  93. char *
  94. res_arp(interface,hardware,target,bp)
  95. struct interface *interface;    /* Pointer to interface block */
  96. int16 hardware;        /* Hardware type */
  97. int32 target;        /* Target IP address */
  98. struct mbuf *bp;    /* IP datagram to be queued if unresolved */
  99. {
  100.     struct arp_tab *arp_lookup(),*arp_add();
  101.     void arp_output();
  102.     register struct arp_tab *arp;
  103.  
  104.     if((arp = arp_lookup(hardware,target)) != NULLARP && arp->state == ARP_VALID)
  105.         return arp->hw_addr;
  106.     /* Create an entry and put the datagram on the
  107.      * queue pending an answer
  108.      */
  109.     arp = arp_add(target,hardware,NULLCHAR,0);
  110.     enqueue(&arp->pending,bp);
  111.     arp_output(interface,hardware,target);
  112.     return NULLCHAR;
  113. }
  114. /* Handle incoming ARP packets. This is almost a direct implementation of
  115.  * the algorithm on page 5 of RFC 826, except for:
  116.  * 1. Outgoing datagrams to unresolved addresses are kept on a queue
  117.  *    pending a reply to our ARP request.
  118.  * 2. The names of the fields in the ARP packet were made more mnemonic.
  119.  */
  120. void
  121. arp_input(interface,bp)
  122. struct interface *interface;
  123. struct mbuf *bp;
  124. {
  125.     struct arp arp;
  126.     struct arp_tab *arp_lookup(),*ap;
  127.     struct arp_type *at;
  128.     struct mbuf *htonarp();
  129.     
  130.     arp_stat.recv++;
  131.     if(ntoharp(&arp,bp) == -1)    /* Convert into host format */
  132.         return;
  133.     if(arp.hardware >= NTYPES){
  134.         /* Unknown hardware type, ignore */
  135.         arp_stat.badtype++;
  136.         return;
  137.     }
  138.     at = &arp_type[arp.hardware];
  139.     if(arp.protocol != at->iptype){
  140.         /* Unsupported protocol type, ignore */
  141.         arp_stat.badtype++;
  142.         return;
  143.     }
  144.     if(arp.hwalen > MAXHWALEN || arp.pralen != sizeof(int32)){
  145.         /* Incorrect protocol addr length (different hw addr lengths
  146.          * are OK since AX.25 addresses can be of variable length)
  147.          */
  148.     arp_stat.badlen++;
  149.         return;
  150.     }
  151.     /* If this guy is already in the table, update its entry
  152.      * unless it's a manual entry (noted by the lack of a timer)
  153.      */
  154.     ap = NULLARP;    /* ap plays the role of merge_flag in the spec */
  155.     if((ap = arp_lookup(arp.hardware,arp.sprotaddr)) != NULLARP
  156.      && ap->timer.start != 0){
  157.         ap = arp_add(arp.sprotaddr,arp.hardware,arp.shwaddr,arp.hwalen & 0xff);
  158.     }
  159.     /* See if we're the address they're looking for */
  160.     if(arp.tprotaddr == ip_addr){
  161.         if(ap == NULLARP)    /* Only if not already in the table */
  162.             arp_add(arp.sprotaddr,arp.hardware,arp.shwaddr,arp.hwalen & 0xff);
  163.  
  164.         if(arp.opcode == ARP_REQUEST){
  165.             /* Swap sender's and target's (us) hardware and protocol
  166.              * fields, and send the packet back as a reply
  167.              */
  168.             bcopy(arp.shwaddr,arp.thwaddr,arp.hwalen);
  169.             /* Mark the end of the sender's AX.25 address
  170.              * in case he didn't
  171.              */
  172.             if(arp.hardware == ARP_AX25)
  173.                 arp.thwaddr[arp.hwalen-1] |= E;
  174.  
  175.             bcopy(interface->hwaddr,arp.shwaddr,at->hwalen);
  176.             arp.tprotaddr = arp.sprotaddr;
  177.             arp.sprotaddr = ip_addr;
  178.             arp.opcode = ARP_REPLY;
  179.             bp = htonarp(&arp);
  180.             (*interface->output)(interface,arp.thwaddr,
  181.                 interface->hwaddr,at->arptype,bp);
  182.             arp_stat.inreq++;
  183.         } else {
  184.             arp_stat.replies++;
  185.         }
  186.     }
  187. }
  188. /* Add an IP-addr / hardware-addr pair to the ARP table */
  189. static
  190. struct arp_tab *
  191. arp_add(ip_addr,hardware,hw_addr,hw_alen)
  192. int32 ip_addr;    /* IP address, host order */
  193. int16 hardware;    /* Hardware type */
  194. char *hw_addr;    /* Hardware address, if known; NULLCHAR otherwise */
  195. int16 hw_alen;    /* Length of hardware address */
  196. {
  197.     char *calloc(),*malloc();
  198.     struct arp_tab *arp_lookup();
  199.     void arp_drop();
  200.     struct mbuf *bp,*dequeue();
  201.     register struct arp_tab *ap;
  202.     register struct arp_type *at;
  203.     unsigned hashval,arp_hash();
  204.  
  205.     at = &arp_type[hardware];
  206.     if((ap = arp_lookup(hardware,ip_addr)) == NULLARP){
  207.         /* New entry */
  208.         if((ap = (struct arp_tab *)calloc(1,sizeof(struct arp_tab))) == NULLARP)
  209.             return NULLARP;
  210.         ap->timer.func = arp_drop;
  211.         ap->timer.arg = (int *)ap;
  212.         ap->hardware = hardware;
  213.         ap->ip_addr = ip_addr;
  214.  
  215.         /* Put on head of hash chain */
  216.         hashval = arp_hash(hardware,ip_addr);
  217.         ap->prev = NULLARP;
  218.         ap->next = arp_tab[hashval];
  219.         arp_tab[hashval] = ap;
  220.         if(ap->next != NULLARP){
  221.             ap->next->prev = ap;
  222.         }
  223.     }
  224.     if(hw_addr == NULLCHAR){
  225.         /* Await response */
  226.         ap->state = ARP_PENDING;
  227.         ap->timer.start = PENDTIME;
  228.     } else {
  229.         /* Response has come in, update entry and run through queue */
  230.         ap->state = ARP_VALID;
  231.         ap->timer.start = ARPLIFE;
  232.         if(ap->hw_addr != NULLCHAR)
  233.             free(ap->hw_addr);
  234.         if((ap->hw_addr = malloc(hw_alen)) == NULLCHAR){
  235.             free((char *)ap);
  236.             return NULLARP;
  237.         }
  238.         bcopy(hw_addr,ap->hw_addr,hw_alen);
  239.         /* This kludge marks the end of an AX.25 address to allow
  240.          * for optional digipeaters (insert Joan Rivers salute here)
  241.          */
  242.         if(hardware == ARP_AX25)
  243.             ap->hw_addr[hw_alen-1] |= E;
  244.         while((bp = dequeue(&ap->pending)) != NULLBUF)
  245.             ip_route(bp,0);
  246.     }
  247.     start_timer(&ap->timer);
  248.     return ap;
  249. }
  250.  
  251. /* Remove an entry from the ARP table */
  252. static
  253. void
  254. arp_drop(ap)
  255. register struct arp_tab *ap;
  256. {
  257.     unsigned arp_hash();
  258.  
  259.     if(ap == NULLARP)
  260.         return;
  261.     stop_timer(&ap->timer);    /* Shouldn't be necessary */
  262.     if(ap->next != NULLARP)
  263.         ap->next->prev = ap->prev;
  264.     if(ap->prev != NULLARP)
  265.         ap->prev->next = ap->next;
  266.     else
  267.         arp_tab[arp_hash(ap->hardware,ap->ip_addr)] = ap->next;
  268.     if(ap->hw_addr != NULLCHAR)
  269.         free(ap->hw_addr);
  270.     free_q(&ap->pending);
  271.     free((char *)ap);
  272. }
  273.  
  274. /* Look up the given IP address in the ARP table */
  275. static
  276. struct arp_tab *
  277. arp_lookup(hardware,ip_addr)
  278. int16 hardware;
  279. int32 ip_addr;
  280. {
  281.     unsigned arp_hash();
  282.     register struct arp_tab *ap;
  283.  
  284.     for(ap = arp_tab[arp_hash(hardware,ip_addr)]; ap != NULLARP; ap = ap->next){
  285.         if(ap->ip_addr == ip_addr && ap->hardware == hardware)
  286.             break;
  287.     }
  288.     return ap;
  289. }
  290. /* Send an ARP request to resolve IP address target_ip */
  291. static
  292. void
  293. arp_output(interface,hardware,target)
  294. struct interface *interface;
  295. int16 hardware;
  296. int32 target;
  297. {
  298.     struct arp arp;
  299.     struct mbuf *bp,*htonarp();
  300.     struct arp_type *at;
  301.  
  302.     at = &arp_type[hardware];
  303.     if(interface->output == NULLFP)
  304.         return;
  305.     
  306.     arp.hardware = hardware;
  307.     arp.protocol = at->iptype;
  308.     arp.hwalen = at->hwalen;
  309.     arp.pralen = sizeof(int32);
  310.     arp.opcode = ARP_REQUEST;
  311.     bcopy(interface->hwaddr,arp.shwaddr,at->hwalen);
  312.     arp.sprotaddr = ip_addr;
  313.     bzero(arp.thwaddr,at->hwalen);
  314.     arp.tprotaddr = target;
  315.     bp = htonarp(&arp);
  316.     (*interface->output)(interface,at->bdcst,
  317.         interface->hwaddr,at->arptype,bp);
  318.     arp_stat.outreq++;
  319. }
  320.  
  321. /* Hash a {hardware type, IP address} pair */
  322. static
  323. unsigned
  324. arp_hash(hardware,ip_addr)
  325. int16 hardware;
  326. int32 ip_addr;
  327. {
  328.     register unsigned hashval;
  329.  
  330.     hashval = hardware;
  331.     hashval ^= hiword(ip_addr);
  332.     hashval ^= loword(ip_addr);
  333.     hashval %= ARPSIZE;
  334.     return hashval;
  335. }        
  336. /* Copy a host format arp structure into mbuf for transmission */
  337. #ifdef    AMIGA
  338. /*
  339.  *  We play some dirty tricks here.  Since the AMIGA is a 68000 based
  340.  *  machine, it doesn't take kindly to doing word and long word stores 
  341.  *  on odd address boundaries.  We'll use bcopy() instead.  We can do
  342.  *  this simply because the 68000 is a big-endian machine, and we don't
  343.  *  need to convert to network byte order.  This is ugly.
  344.  */
  345. #endif
  346. static
  347. struct mbuf *
  348. htonarp(arp)
  349. register struct arp *arp;
  350. {
  351.     struct mbuf *bp;
  352.     register char *buf;
  353.  
  354.     if(arp == (struct arp *)NULL)
  355.         return NULLBUF;
  356.     if((bp = alloc_mbuf(sizeof(struct arp))) == NULLBUF)
  357.         return NULLBUF;
  358.  
  359.     buf = bp->data;
  360.  
  361.     *(int16 *)buf = htons(arp->hardware);
  362.     buf += sizeof(int16);
  363.  
  364.     *(int16 *)buf = htons(arp->protocol);
  365.     buf += sizeof(int16);
  366.  
  367.     *buf++ = arp->hwalen;
  368.  
  369.     *buf++ = arp->pralen;
  370.  
  371.     *(int16 *)buf = htons(arp->opcode);
  372.     buf += sizeof(int16);
  373.  
  374.     bcopy(arp->shwaddr,buf,arp->hwalen);
  375.     buf += arp->hwalen;
  376.  
  377. #ifndef    AMIGA
  378.     *(int32 *)buf = htonl(arp->sprotaddr);
  379. #else
  380.     /* we've been alright up to now, but arp->hwalen may have been
  381.        odd, so we don't know if buf is word aligned any more! */
  382.     bcopy(&arp->sprotaddr, buf, sizeof(int32));
  383. #endif
  384.     buf += sizeof(int32);
  385.  
  386.     bcopy(arp->thwaddr,buf,arp->hwalen);
  387.     buf += arp->hwalen;
  388.  
  389. #ifndef    AMIGA
  390.     *(int32 *)buf = htonl(arp->tprotaddr);
  391. #else
  392.     bcopy(&arp->tprotaddr, buf, sizeof(int32));
  393. #endif
  394.     buf += sizeof(int32);
  395.  
  396.     bp->cnt = buf - bp->data;
  397.     return bp;
  398. }
  399. /* Convert an incoming ARP packet into a host-format structure */
  400. static
  401. int
  402. ntoharp(arp,bp)
  403. register struct arp *arp;
  404. struct mbuf *bp;
  405. {
  406.     if(arp == (struct arp *)NULL || bp == NULLBUF)
  407.         return -1;
  408.  
  409.     pullup(&bp,(char *)&arp->hardware,sizeof(int16));
  410.     arp->hardware = ntohs(arp->hardware);
  411.  
  412.     pullup(&bp,(char *)&arp->protocol,sizeof(int16));
  413.     arp->protocol = ntohs(arp->protocol);
  414.  
  415.     pullup(&bp,(char *)&arp->hwalen,sizeof(char));
  416.  
  417.     pullup(&bp,(char *)&arp->pralen,sizeof(char));
  418.  
  419.     pullup(&bp,(char *)&arp->opcode,sizeof(int16));
  420.     arp->opcode = ntohs(arp->opcode);
  421.  
  422.     pullup(&bp,arp->shwaddr,arp->hwalen);
  423.  
  424.     pullup(&bp,(char *)&arp->sprotaddr,sizeof(int32));
  425.     arp->sprotaddr = ntohl(arp->sprotaddr);
  426.  
  427.     pullup(&bp,arp->thwaddr,arp->hwalen);
  428.  
  429.     pullup(&bp,(char *)&arp->tprotaddr,sizeof(int32));
  430.     arp->tprotaddr = ntohl(arp->tprotaddr);
  431.  
  432.     free_p(bp);
  433.     return 0;
  434. }
  435. #ifdef    TRACE
  436. char *arptypes[] = {
  437.     NULLCHAR,
  438.     "Ethernet",
  439.     "Exp Ethernet",
  440.     "AX.25",
  441.     "Pronet",
  442.     "Chaos"
  443. };
  444. int doarpadd(),doarpdrop();
  445. struct cmds arpcmds[] = {
  446.     "add", doarpadd, 4,
  447.     "usage: arp add <ip addr> ether|ax25 <callsign|ether addr>",
  448.     "arp add failed",
  449.  
  450.     "drop", doarpdrop, 3,
  451.     "usage: arp drop <ip addr> ether|ax25",
  452.     "not in table",
  453.  
  454.     NULLCHAR, NULLFP, 0,
  455.     "arp subcommands: add, drop",
  456.     NULLCHAR, 
  457. };
  458. int
  459. doarp(argc,argv)
  460. int argc;
  461. char *argv[];
  462. {
  463.     if(argc < 2){
  464.         dumparp();
  465.         return 0;
  466.     }
  467.     return subcmd(arpcmds,argc,argv);
  468. }
  469. static
  470. doarpadd(argc,argv)
  471. int argc;
  472. char *argv[];
  473. {
  474.     int hardware,hwalen,i;
  475.     int32 addr,aton();
  476.     char *malloc(),*hwaddr;
  477.     int naddr;
  478.     struct arp_tab *ap;
  479.     struct arp_type *at;
  480.     struct ax25_addr *axp;
  481.  
  482.     addr = aton(argv[1]);
  483.     /* This is a kludge. It really ought to be table driven */
  484.     switch(tolower(argv[2][0])){
  485.     case 'e':    /* "ether" */
  486.         hardware = ARP_ETHER;
  487.         naddr = 1;
  488.         break;        
  489.     case 'a':    /* "ax25" */
  490.         hardware = ARP_AX25;
  491.         naddr = argc - 3;
  492.         break;
  493.     default:
  494.         printf("unknown hardware type \"%s\"\r\n",argv[2]);
  495.         return -1;
  496.     }
  497.     /* If an entry already exists, clear it */
  498.     if((ap = arp_lookup(hardware,addr)) != NULLARP)
  499.         arp_drop(ap);
  500.  
  501.     at = &arp_type[hardware];
  502.  
  503.     /* Allocate buffer for hardware address and fill with remaining args */
  504.     hwalen = at->hwalen * naddr;
  505.     if((hwaddr = malloc(hwalen)) == NULLCHAR){
  506.         printf("No space\r\n");
  507.         return 0;
  508.     }
  509.     (*at->scan)(hwaddr,argv[3]);    /* Destination address */
  510.  
  511.     /* Special hackery to handle a series of AX.25 digipeaters */
  512.     if(hardware == ARP_AX25){
  513.         axp = (struct ax25_addr *)hwaddr;
  514.         for(i=1;i<naddr;i++){
  515.             /* Set E bit only on last AX.25 call */
  516.             axp->ssid &= ~E;
  517.             /* axp++; */
  518.             axp = (struct ax25_addr *) ((char *)axp + AXALEN);
  519.             (*at->scan)((char *)axp,argv[3+i]);
  520.         }
  521.     }
  522.     ap = arp_add(addr,hardware,hwaddr,hwalen);    /* Put in table */
  523.     free(hwaddr);                    /* Clean up */
  524.     stop_timer(&ap->timer);            /* Make entry permanent */
  525.     ap->timer.count = ap->timer.start = 0;
  526. }
  527. /* Remove an ARP entry */
  528. static
  529. doarpdrop(argc,argv)
  530. int argc;
  531. char *argv[];
  532. {
  533.     int hardware;
  534.     int32 addr,aton();
  535.     struct arp_tab *ap;
  536.  
  537.     addr = aton(argv[1]);
  538.     /* This is a kludge. It really ought to be table driven */
  539.     switch(tolower(argv[2][0])){
  540.     case 'e':    /* "ether" */
  541.         hardware = ARP_ETHER;
  542.         break;        
  543.     case 'a':    /* "ax25" */
  544.         hardware = ARP_AX25;
  545.         break;
  546.     default:
  547.         hardware = 0;
  548.         break;
  549.     }
  550.     if((ap = arp_lookup(hardware,addr)) == NULLARP)
  551.         return -1;
  552.     arp_drop(ap);
  553.     return 0;    
  554. }
  555. /* Dump ARP table */
  556. static
  557. dumparp()
  558. {
  559.     register int i;
  560.     extern struct arp_stat arp_stat;
  561.     register struct arp_tab *ap;
  562.     char e[128];
  563.     char *inet_ntoa();
  564.  
  565.     printf("received %u badtype %u reqst in %u replies %u reqst out %u\r\n",
  566.      arp_stat.recv,arp_stat.badtype,arp_stat.inreq,
  567.      arp_stat.replies,arp_stat.outreq);
  568.  
  569.     printf("IP addr         Type     Time Q Addr\r\n");
  570.     for(i=0;i<ARPSIZE;i++){
  571.         for(ap = arp_tab[i];ap != (struct arp_tab *)NULL;ap = ap->next){
  572.             printf("%-16s",inet_ntoa(ap->ip_addr));
  573.             printf("%-9s",arptypes[ap->hardware]);
  574.             printf("%-5ld",ap->timer.count*(long)MSPTICK/1000);
  575.             if(ap->state == ARP_PENDING)
  576.                 printf("%-2u",len_q(ap->pending));
  577.             else
  578.                 printf("  ");
  579.             if(ap->state == ARP_VALID){
  580.                 if(arp_type[ap->hardware].format != NULLFP){
  581.                     (*arp_type[ap->hardware].format)(e,ap->hw_addr);
  582.                 } else {
  583.                     e[0] = '\0';
  584.                 }
  585.                 printf("%s",e);
  586.             } else {
  587.                 printf("[unknown]");
  588.             }
  589.             printf("\r\n");
  590.         }
  591.     }
  592.     return 0;
  593. }
  594. /* Dump ARP packets (incomplete) */
  595. static char *hwtypes[] = {
  596.     "",
  597.     "10 Mb Ethernet",
  598.     "3 Mb Ethernet",
  599.     "AX.25",
  600.     NULLCHAR,
  601. };
  602. #define    NHWTYPES 4
  603. arp_dump(bp)
  604. struct mbuf *bp;
  605. {
  606.     struct arp arp;
  607.     struct mbuf *tbp;
  608.     char *inet_ntoa();
  609.  
  610.     if(bp == NULLBUF)
  611.         return;
  612.     /* Make temporary copy  */
  613.     dup_p(&tbp,bp,0,len_mbuf(bp));
  614.     ntoharp(&arp,tbp);
  615.  
  616.     if(arp.hardware < NHWTYPES)
  617.         printf("ARP: hwtype %s",hwtypes[arp.hardware]);
  618.     else
  619.         printf("ARP: hwtype %u",arp.hardware);
  620.     printf(" prot 0x%x hwlen %u prlen %u",
  621.         arp.protocol,arp.hwalen,arp.pralen);
  622.     switch(arp.opcode){
  623.     case ARP_REQUEST:
  624.         printf(" op REQUEST");
  625.         break;
  626.     case ARP_REPLY:
  627.         printf(" op REPLY");
  628.         break;
  629.     default:
  630.         printf(" op %u",arp.opcode);
  631.         break;
  632.     }
  633.     printf(" target %s\r\n",inet_ntoa(arp.tprotaddr));
  634. }
  635. #endif
  636. SHAR_EOF
  637. cat << \SHAR_EOF > ax25.c
  638. /* Vestigial AX.25 link layer, understands only UI frames */
  639.  
  640. #include <stdio.h>
  641. #include "machdep.h"
  642. #include "mbuf.h"
  643. #include "iface.h"
  644. #include "timer.h"
  645. #include "arp.h"
  646. #include "slip.h"
  647. #include "ax25.h"
  648. #include <ctype.h>
  649.  
  650. #ifdef    TRACE
  651. #include "trace.h"
  652. #endif
  653.  
  654. /* AX.25 broadcast address: "QST-0" in shifted ascii */
  655. struct ax25_addr ax25_bdcst = {
  656.     'Q'<<1, 'S'<<1, 'T'<<1, ' '<<1, ' '<<1, ' '<<1,
  657.     ('0'<<1) | E,
  658. };
  659. struct ax25_addr mycall;
  660. int digipeat;        /* Controls digipeating */
  661.  
  662. /* Send IP datagrams in AX.25 UI frames using ARP */
  663. int
  664. ax_send(bp,interface,gateway,precedence,delay,throughput,reliability)
  665. struct mbuf *bp;
  666. struct interface *interface;
  667. int32 gateway;
  668. char precedence;
  669. char delay;
  670. char throughput;
  671. char reliability;
  672. {
  673.     char *hw_addr,*res_arp();
  674.  
  675.     hw_addr = res_arp(interface,ARP_AX25,gateway,bp);
  676.     if(hw_addr != NULLCHAR)
  677.         (*interface->output)(interface,(struct ax25_addr *)hw_addr,
  678.             interface->hwaddr,PID_IP,bp);
  679. }
  680. /* Send a raw packet to the KISS TNC using AX.25 link header.
  681.  * Note that the calling order here must match ec_output
  682.  * since ARP also uses it.
  683.  */
  684. kiss_output(interface,dest,src,pid,bp)
  685. struct interface *interface;
  686. struct ax25_addr *dest;    /* Destination AX.25 address (7 bytes, shifted) */
  687. struct ax25_addr *src;    /* Source AX.25 address (7 bytes, shifted) */
  688. char pid;        /* Protocol ID */
  689. struct mbuf *bp;    /* Data field (follows PID) */
  690. {
  691.     register struct mbuf *hbp;
  692.     struct mbuf *ax_encode();
  693.     struct slip *sp;
  694.  
  695.     if((bp = ax_encode(dest,src,pid,bp)) == NULLBUF)
  696.         return;
  697. #ifdef    TRACE
  698.     if(trace & TRACE_AX25){
  699.         printf("%s sent:\r\n",interface->name);
  700.         if((trace & TRACE_HDR) > 1)
  701.             ax25_dump(bp);
  702.         if(trace & TRACE_DUMP)
  703.             hexdump(bp);
  704.         if(trace & TRACE_ASCII)
  705.             asciidump(bp);
  706.         fflush(stdout);
  707.     }
  708. #endif
  709.     /* Put type field for KISS TNC on front */
  710.     if((hbp = alloc_mbuf(1)) == NULLBUF){
  711.         free_p(bp);
  712.         return;
  713.     }
  714.     *hbp->data = 0;
  715.     hbp->cnt = 1;
  716.     hbp->next = bp;
  717.     slipq(interface->dev,hbp);
  718. }
  719.  
  720. /* Put a AX.25 header on the front of a packet */
  721. struct mbuf *
  722. ax_encode(dest,src,pid,bp)
  723. struct ax25_addr *dest;    /* Destination AX.25 address (7 bytes, shifted) */
  724. struct ax25_addr *src;    /* Source AX.25 address (7 bytes, shifted) */
  725. char pid;        /* Protocol ID */
  726. struct mbuf *bp;    /* Data field (follows PID) */
  727. {
  728.     register struct ax25_addr *ap;
  729.     struct mbuf *abp;
  730.     char *cp;
  731.     int ndigi;
  732.     int16 hdr_len;
  733.  
  734.     /* Determine length of dest addr */
  735.     for(ndigi = 0,ap = dest; (ap->ssid & E) == 0;
  736.             ap = (struct ax25_addr *) ((char *)ap + AXALEN))
  737.         ndigi++;
  738.  
  739.     /* Compute header length:
  740.      * 2 AX.25 address fields for source and dest +
  741.      * "ndigi" AX.25 address field(s) for digipeaters +
  742.      * 2 bytes for control and PID fields
  743.      */
  744.  
  745.     hdr_len = (2 + ndigi)*AXALEN + 2;
  746.  
  747.     /* Create AX.25 link level header */
  748.     if((abp = alloc_mbuf(hdr_len)) == NULLBUF){
  749.         free_p(bp);
  750.         return NULLBUF;
  751.     }
  752.     abp->cnt = hdr_len;
  753.  
  754.     /* Now fill it in */
  755.  
  756.     ap = (struct ax25_addr *)(abp->data);
  757.  
  758.     bcopy((char *)dest,(char *)ap,AXALEN);
  759.     ap->ssid &= ~E;
  760.     ap = (struct ax25_addr *) ((char *)ap + AXALEN);
  761.     dest = (struct ax25_addr *) ((char *)dest + AXALEN);
  762.  
  763.     bcopy((char *)src,(char *)ap,AXALEN);
  764.     ap->ssid &= ~E;
  765.     cp = (char *)ap;    /* get pointer to last address (source) */
  766.     ap = (struct ax25_addr *) ((char *)ap + AXALEN);
  767.  
  768.     while(ndigi-- != 0) {
  769.         bcopy((char *)dest,(char *)ap,AXALEN);
  770.         dest = (struct ax25_addr *) ((char *)dest + AXALEN);
  771.         cp = (char *)ap;
  772.         ap = (struct ax25_addr *) ((char *)ap + AXALEN);
  773.     }
  774.     ((struct ax25_addr *)cp)->ssid |= E;    /* Mark end of address field */    
  775.  
  776.     cp = (char *)ap;    /* Point to first byte past address field */
  777.     *cp++ = UI;
  778.     *cp++ = pid;
  779.  
  780.     abp->next = bp;        /* Link in data field */
  781.     return abp;
  782. }
  783. /* Process incoming KISS TNC frame */
  784. kiss_recv(interface,bp)
  785. struct interface *interface;
  786. struct mbuf *bp;
  787. {
  788.     pullup(&bp,NULLCHAR,1);    /* remove KISS TNC type field */
  789.     if(bp != NULLBUF)
  790.         ax_recv(interface,bp);
  791. }
  792. /* Process incoming AX.25 packets.
  793.  * After optional tracing, the address field is examined. If it is
  794.  * directed to us as a digipeater, repeat it.  If it is addressed to
  795.  * us or to QST-0, kick it upstairs depending on the protocol ID.
  796.  */
  797. int
  798. ax_recv(interface,bp)
  799. struct interface *interface;
  800. struct mbuf *bp;
  801. {
  802.     void arp_input(),ip_route();
  803.     struct ax25_addr *ap,*ap1;
  804.     char pid,multicast,ours,*control,*cbyte();
  805.     int addrsize;
  806.     struct mbuf *hbp;
  807.  
  808. #ifdef    TRACE
  809.     if(trace & TRACE_AX25){
  810.         printf("%s recv:\r\n",interface->name);
  811.         if((trace & TRACE_HDR) > 1)
  812.             ax25_dump(bp);
  813.         if(trace & TRACE_DUMP)
  814.             hexdump(bp);
  815.         if(trace & TRACE_ASCII)
  816.             asciidump(bp);
  817.         fflush(stdout);
  818.     }
  819. #endif
  820.     control = cbyte(bp);        /* control -> control byte */
  821.  
  822.     ap = (struct ax25_addr *)bp->data;    /* -> address field */
  823.     addrsize = control - (char *)ap;    /* # bytes in address field */
  824.     /* Check for either a missing control byte or a residual length
  825.      * address field
  826.      */
  827.     if(control == NULL || addrsize % AXALEN != 0){
  828.         free_p(bp);
  829.         return;
  830.     }
  831.     addrsize /= AXALEN; /* # addresses in address field */
  832.     /* Check for invalid address field (too short or odd length) */    
  833.     if(addrsize < 2) {
  834.         free_p(bp);
  835.         return;
  836.     }
  837.     /* Rescan, looking for our call in the repeater fields, if any.
  838.      * Repeat appropriate packets.
  839.      */
  840.     /* for(ap1 = &ap[2]; ap1 < &ap[addrsize]; ap1++){ */
  841.  
  842.     for(ap1 = (struct ax25_addr *) ((char *)ap + 2*AXALEN);
  843.         ap1 < (struct ax25_addr *) ((char *)ap + addrsize*AXALEN);
  844.         ap1 = (struct ax25_addr *) ((char *)ap1 + AXALEN)) {
  845.  
  846.         if((ap1->ssid & REPEATED) == 0){
  847.             /* Check if packet is directed to us as a digipeater */
  848.             if(digipeat && addreq(ap1,&mycall)){
  849.                 /* Yes, kick it back out */
  850.                 ap1->ssid |= REPEATED;
  851.                 /* Put type field for KISS TNC on front */
  852.                 if((hbp = alloc_mbuf(1)) == NULLBUF){
  853.                     free_p(bp);
  854.                     return;
  855.                 }
  856.                 *hbp->data = 0;
  857.                 hbp->cnt = 1;
  858.                 hbp->next = bp;
  859.                 slipq(interface->dev,hbp);
  860.             } else {
  861.                 /* Addressed to some other digipeater */
  862.                 free_p(bp);
  863.             }
  864.             return;
  865.         }
  866.     }
  867.     /* Packet has passed all repeaters, now look at destination */
  868.     ours = 0;
  869.     if(addreq(&ap[0],&ax25_bdcst)){
  870.         multicast = 1;    /* Broadcast packet */
  871.     } else if(addreq(&ap[0],&mycall)){
  872.         multicast = 0;    /* Packet directed at us */
  873.         ours = 1;
  874. #if    0
  875.     /* we really do want to see all of the packets, later on */
  876.     } else {
  877.         /* Not for us */
  878.         free_p(bp);
  879.         return;
  880. #endif
  881.     }
  882.     /* Now remove the header and the control field. Note: This will
  883.      * have to be changed if the connected mode of AX.25 (ugh!) is ever
  884.      * implemented
  885.      */
  886.     pullup(&bp,NULLCHAR,1 + addrsize * AXALEN);
  887.  
  888.     /* Examine the protocol ID field and switch to the right protocol */
  889.     if(pullup(&bp,&pid,1) != 1)
  890.         return;    /* No PID, probably not an I-frame */
  891.     switch(pid & 0xff){
  892.     case PID_ARP:
  893.         arp_input(interface,bp);
  894.         break;
  895.     case PID_IP:
  896.         if (!(ours || multicast)) {
  897.             free_p(bp);
  898.             break;
  899.         }
  900.         ip_route(bp,multicast);
  901.         break;
  902. #ifdef    NETROM
  903.     case PID_NETROM:
  904.         netrom_input(interface, bp);
  905.         break;
  906. #endif
  907.     default:
  908.         free_p(bp);
  909.         break;
  910.     }
  911. }
  912. /* Display or change our AX.25 address */
  913. domycall(argc,argv)
  914. int argc;
  915. char *argv[];
  916. {
  917.     char buf[15];
  918.  
  919.     if(argc < 2){
  920.         pax25(buf,&mycall);
  921.         printf("%s\r\n",buf);
  922.         return 0;
  923.     }
  924.     if(setcall(&mycall,argv[1]) == -1)
  925.         return -1;
  926.     mycall.ssid |= E;
  927. }
  928. /*
  929.  * setcall - convert callsign plus substation ID of the form
  930.  * "KA9Q-0" to AX.25 (shifted) address format
  931.  *   Address extension bit is left clear
  932.  *   Return -1 on error, 0 if OK
  933.  */
  934. int
  935. setcall(out,call)
  936. struct ax25_addr *out;
  937. char *call;
  938. {
  939.     int csize;
  940.     unsigned ssid;
  941.     register int i;
  942.     register char *cp,*dp;
  943.     char c,*index();
  944.  
  945.     if(out == (struct ax25_addr *)NULL || call == NULLCHAR || *call == '\0'){
  946.         return -1;
  947.     }
  948.     /* Find dash, if any, separating callsign from ssid
  949.      * Then compute length of callsign field and make sure
  950.      * it isn't excessive
  951.      */
  952.     dp = index(call,'-');
  953.     if(dp == NULLCHAR)
  954.         csize = strlen(call);
  955.     else
  956.         csize = dp - call;
  957.     if(csize > 6)
  958.         return -1;
  959.     /* Now find and convert ssid, if any */
  960.     if(dp != NULLCHAR){
  961.         dp++;    /* skip dash */
  962.         ssid = atoi(dp);
  963.         if(ssid > 15)
  964.             return -1;
  965.     } else
  966.         ssid = 0;
  967.     /* Copy upper-case callsign, left shifted one bit */
  968.     cp = out->call;
  969.     for(i=0;i<csize;i++){
  970.         c = *call++;
  971.         if(islower(c))
  972.             c = toupper(c);
  973.         *cp++ = c << 1;
  974.     }
  975.     /* Pad with shifted spaces if necessary */
  976.     for(;i<6;i++)
  977.         *cp++ = ' ' << 1;
  978.     
  979.     /* Insert substation ID field and set reserved bits */
  980.     out->ssid = 0x60 | (ssid << 1);
  981.     return 0;
  982. }
  983. static
  984. addreq(a,b)
  985. register struct ax25_addr *a,*b;
  986. {
  987.     if(bcmp(a->call,b->call,ALEN) != 0)
  988.         return 0;
  989.     if((a->ssid & SSID) != (b->ssid & SSID))
  990.         return 0;
  991.     return 1;
  992. }
  993. /* Convert encoded AX.25 address to printable string */
  994. pax25(e,addr)
  995. char *e;
  996. struct ax25_addr *addr;
  997. {
  998.     register int i;
  999.     char c,*cp;
  1000.  
  1001.     cp = addr->call;
  1002.     for(i=6;i != 0;i--){
  1003.         c = (*cp++ >> 1) & 0x7f;
  1004.         if(c == ' ')
  1005.             break;
  1006.         *e++ = c;
  1007.     }
  1008.     sprintf(e,"-%d",(addr->ssid >> 1) & 0xf);    /* ssid */
  1009. }
  1010. /* Print a string of AX.25 addresses in the form
  1011.  * "KA9Q-0 [via N4HY-0,N2DSY-2]"
  1012.  */
  1013. psax25(e,addr)
  1014. register char *e;
  1015. register struct ax25_addr *addr;
  1016. {
  1017.     int i;
  1018.  
  1019.     for(i=0;;i++){
  1020.         pax25(e,addr);
  1021.         if(addr->ssid & E)
  1022.             break;
  1023.         if(i == 0)
  1024.             strcat(e," via ");
  1025.         else
  1026.             strcat(e,",");
  1027.         e += strlen(e);
  1028.         /* addr++; */
  1029.         addr = (struct ax25_addr *) ((char *)addr + AXALEN);
  1030.     }
  1031. }
  1032. /* Return a pointer to the control byte in the given frame */
  1033. char *
  1034. cbyte(fp)
  1035. register struct mbuf *fp;
  1036. {
  1037.     register char *cp;
  1038.     register unsigned cnt;
  1039.  
  1040.     if(fp == NULLBUF || fp->data == NULLCHAR)
  1041.         return NULLCHAR;
  1042.  
  1043.     cnt = fp->cnt;
  1044.     cp = fp->data;
  1045.     while(cnt != 0 && (*cp & E) == 0){
  1046.         cnt--;
  1047.         cp++;
  1048.     }
  1049.     /* If the address field never ended, cnt = 0; if it ended
  1050.      * on the last byte of a frame, cnt = 1. In either case,
  1051.      * there is no control field
  1052.      */
  1053.     if(cnt <= 1)
  1054.         return NULLCHAR;
  1055.     else
  1056.         return cp + 1;
  1057. }
  1058. dokiss(argc,argv)
  1059. int argc;
  1060. char *argv[];
  1061. {
  1062.     struct interface *ifp;
  1063.     struct mbuf *hbp;
  1064.     int i;
  1065.     char *cp;
  1066.  
  1067.     if(argc < 2){
  1068.         printf("Interface name missing\r\n");
  1069.         return 1;
  1070.     }
  1071.     for(ifp=ifaces;ifp != NULLIF;ifp = ifp->next){
  1072.         if(strcmp(argv[1],ifp->name) == 0)
  1073.             break;
  1074.     }
  1075.     if(ifp == NULL){
  1076.         printf("Interface \"%s\" unknown\r\n",argv[1]);
  1077.         return 1;
  1078.     }
  1079.     if(ifp->output != kiss_output){
  1080.         printf("Interface \"%s\" not kiss\r\n",argv[1]);
  1081.         return 1;
  1082.     }
  1083.     if(argc < 3){
  1084.         printf("Data field missing\r\n");
  1085.         return 1;
  1086.     }
  1087.     /* Number of bytes in message == number of args - 2, since
  1088.      * first two args are "kiss" and the interface name
  1089.      */
  1090.     if((hbp = alloc_mbuf(argc - 2)) == NULLBUF){
  1091.         free_p(hbp);
  1092.         return;
  1093.     }
  1094.     hbp->cnt = argc - 2;
  1095.     hbp->next = NULL;
  1096.     for(i=2,cp = hbp->data;i < argc;i++,cp++){
  1097.         *cp = htoi(argv[i]);
  1098.     }
  1099.     slipq(ifp->dev,hbp);
  1100. }
  1101. #ifdef    TRACE
  1102.  
  1103. /* Dump an AX.25 packet header */
  1104. ax25_dump(abp)
  1105. struct mbuf *abp;
  1106. {
  1107.     struct mbuf *bp;
  1108.     struct ax25_addr src,dest,addr;
  1109.     char tmp[20],*cp,control,cmdrsp,pid;
  1110.     int16 len,type,ftype();
  1111.  
  1112.     dup_p(&bp,abp,0,len_mbuf(abp));
  1113.  
  1114.     /* Read and print the destination and source addresses */
  1115.     if(pullup(&bp,(char *)&dest,AXALEN) != AXALEN)
  1116.         goto quit;
  1117.     if(pullup(&bp,(char *)&src,AXALEN) != AXALEN)
  1118.         goto quit;
  1119.  
  1120.     pax25(tmp,&src);
  1121.     printf("AX25: %s",tmp);
  1122.     pax25(tmp,&dest);
  1123.     printf("->%s",tmp);
  1124.  
  1125.     if((src.ssid & E) == 0){
  1126.         /* Find the last address entry in the digi string */
  1127.         printf(" v");
  1128.         while(pullup(&bp,(char *)&addr,AXALEN) == AXALEN){
  1129.             pax25(tmp,&addr);
  1130. #ifdef    AMIGA
  1131.             /* this is just personal preference; reminds me of 
  1132.                the WA8DED TNC-1 code */
  1133.             printf(" %s%s",tmp,(addr.ssid & REPEATED) ? "*":"");
  1134. #else
  1135.             printf(" %s%s",tmp,(addr.ssid & REPEATED) ? "H":"");
  1136. #endif
  1137.             if(addr.ssid & E)
  1138.                 break;    /* Found it */
  1139.         }
  1140.     }
  1141.     if(pullup(&bp,&control,1) != 1)
  1142.         goto quit;
  1143.  
  1144.     putchar(' ');
  1145.     type = ftype(control);
  1146.     switch(type){
  1147.     case I:
  1148.         printf("I");
  1149.         break;
  1150.     case SABM:
  1151.         printf("SABM");
  1152.         break;
  1153.     case DISC:
  1154.         printf("DISC");
  1155.         break;
  1156.     case DM:
  1157.         printf("DM");
  1158.         break;
  1159.     case UA:
  1160.         printf("UA");
  1161.         break;
  1162.     case RR:
  1163.         printf("RR");
  1164.         break;
  1165.     case RNR:
  1166.         printf("RNR");
  1167.         break;
  1168.     case REJ:
  1169.         printf("REJ");
  1170.         break;
  1171.     case FRMR:
  1172.         printf("FRMR");
  1173.         break;
  1174.     case UI:
  1175.         printf("UI");
  1176.         break;
  1177.     default:
  1178.         printf("[invalid]");
  1179.     }
  1180.  
  1181.     if((dest.ssid & C) != (src.ssid & C)){
  1182.         if(dest.ssid & C)
  1183.             cmdrsp = COMMAND;
  1184.         else
  1185.             cmdrsp = RESPONSE;
  1186.     } else 
  1187.         cmdrsp = UNKNOWN;
  1188.     /* Dump poll/final bit */
  1189.     if(control & PF){
  1190.         switch(cmdrsp){
  1191.         case COMMAND:
  1192.             printf("(P)");
  1193.             break;
  1194.         case RESPONSE:
  1195.             printf("(F)");
  1196.             break;
  1197.         default:
  1198.             printf("(P/F)");
  1199.             break;
  1200.         }
  1201.     }
  1202.     /* Dump sequence numbers */
  1203.     if((type & 0x3) != U)    /* I or S frame? */
  1204.         printf(" NR=%d",(control>>5)&7);
  1205.     if((type & 0x1) == I)    /* I frame? */
  1206.         printf(" NS=%d",(control>>1)&7);
  1207.  
  1208.     if(pullup(&bp,&pid,1) != 1)
  1209.         goto quit;
  1210.     printf(" pid 0x%x\r\n",pid & 0xff);
  1211.     if((trace & TRACE_HDR) > 2){
  1212.         switch(pid & 0xff){
  1213.         case PID_ARP:
  1214.             arp_dump(bp);
  1215.             break;
  1216.         case PID_IP:
  1217.             ip_dump(bp);
  1218.             break;
  1219. #ifdef    NETROM
  1220.         case PID_NETROM:
  1221.             netrom_dump(bp);
  1222.             break;
  1223. #endif
  1224.         }
  1225.     }
  1226.     free_p(bp);
  1227.     fflush(stdout);
  1228.     return;
  1229. quit:    /* I hate go-to's, but sometimes there's no real choice */
  1230.     free_p(bp);
  1231.     printf("\r\n");
  1232.     fflush(stdout);
  1233. }
  1234. /* Figure out the frame type from the control field
  1235.  * This is done by masking out any sequence numbers and the
  1236.  * poll/final bit after determining the general class (I/S/U) of the frame
  1237.  */
  1238. static
  1239. int16
  1240. ftype(control)
  1241. register char control;
  1242. {
  1243.     if((control & 1) == 0)    /* An I-frame is an I-frame... */
  1244.         return I;
  1245.     if(control & 2)            /* U-frames use all except P/F bit for type */
  1246.         return(control & ~PF);
  1247.     else                    /* S-frames use low order 4 bits for type */
  1248.         return(control & 0xf);
  1249. }
  1250. #endif
  1251. SHAR_EOF
  1252. cat << \SHAR_EOF > ftpcli.c
  1253. /* FTP client (interactive user) code */
  1254. #include <stdio.h>
  1255. #include "machdep.h"
  1256. #include "mbuf.h"
  1257. #include "netuser.h"
  1258. #include "icmp.h"
  1259. #include "timer.h"
  1260. #include "tcp.h"
  1261. #include "ftp.h"
  1262. #include "session.h"
  1263. #include "cmdparse.h"
  1264. #include "dos.h"
  1265.  
  1266. extern struct session *current;
  1267. extern char nospace[];
  1268. #define MAXFILENAME    32
  1269. char multiarg[NARG][MAXFILENAME];
  1270. int multiargc = 0, curmulti = 0;
  1271. struct FileInfoBlock info;
  1272. int dodfind = 1;
  1273. int docd(),dodir(),doget(),dols(),doput(),dotype(),doabort(), domput();
  1274.  
  1275. struct cmds ftpabort[] = {
  1276.     "abort",     doabort, 0, NULLCHAR, NULLCHAR,
  1277.     NULLCHAR,    NULLFP,  0, "Only valid command is \"abort\"", NULLCHAR,
  1278. };
  1279.  
  1280. struct cmds ftpcmds[] = {
  1281.     "cd",    docd,   2,    "cd <directory>", "Could not change directory",
  1282.     "dir",    dodir,  0,    NULLCHAR,         "Could not complete dir",
  1283.     "list",    dodir,  0,    NULLCHAR,         "Could not complete dir",
  1284.     "get",    doget,  0,    NULLCHAR,         "Could not complete get",
  1285.     "ls",    dols,   0,    NULLCHAR,         "Could not complete ls",
  1286.     "mput", domput,    0,    NULLCHAR,     "Could not complete mput",
  1287.     "nlst",    dols,   0,    NULLCHAR,         "Could not complete ls",
  1288.     "put",    doput,  0,    NULLCHAR,         "Could not complete put",
  1289.     "type",    dotype, 0,    NULLCHAR,         "Could not complete type",
  1290.     NULLCHAR,    NULLFP, 0, NULLCHAR, NULLCHAR,
  1291. };
  1292.  
  1293. /* Handle top-level FTP command */
  1294. doftp(argc,argv)
  1295. int argc;
  1296. char *argv[];
  1297. {
  1298.     int32 aton();
  1299.     int ftpparse();
  1300.     char *inet_ntoa();
  1301.     void r_ctl(),s_ctl();
  1302.     struct session *s,*newsession();
  1303.     struct ftp *ftp,*ftp_create();
  1304.     struct tcb *tcb;
  1305.     struct socket lsocket,fsocket;
  1306.  
  1307.     lsocket.address = ip_addr;
  1308.     lsocket.port = lport++;
  1309.     fsocket.address = aton(argv[1]);
  1310.     if(fsocket.address == ip_addr){
  1311.         printf("FTPing to yourself not supported\r\n");
  1312.         return 1;
  1313.     }
  1314.     if(argc < 3)
  1315.         fsocket.port = FTP_PORT;
  1316.     else
  1317.         fsocket.port = atoi(argv[2]);
  1318.  
  1319.     /* Allocate a session control block */
  1320.     if((s = newsession()) == NULLSESSION){
  1321.         printf("Too many sessions\r\n");
  1322.         return 1;
  1323.     }
  1324.     current = s;
  1325.     s->type = FTP;
  1326.     s->parse = ftpparse;
  1327.  
  1328.     /* Allocate an FTP control block */
  1329.     if((ftp = ftp_create(0)) == NULLFTP){
  1330.         s->type = FREE;
  1331.         printf(nospace);
  1332.         return 1;
  1333.     }
  1334.     ftp->state = COMMAND_STATE;
  1335.     s->cb.ftp = ftp;    /* Downward link */
  1336.     ftp->session = s;    /* Upward link */
  1337.  
  1338.     /* Now open the control connection */
  1339.     tcb = open_tcp(&lsocket,&fsocket,TCP_ACTIVE,
  1340.         0,r_ctl,NULLVFP,s_ctl,0,(int *)ftp);
  1341.     if(tcb == NULLTCB || tcb->state == CLOSED){
  1342.         /* This is actually a bit dirty here. About the only time the
  1343.          * state will ever be closed here is if we tried to connect to
  1344.          * ourselves and got RST'ed.  If this is true then the close
  1345.          * upcall will already have freed the TCB and the FTP block,
  1346.          * so we're looking at the TCB after it's been freed.
  1347.          */
  1348.         s->type = FREE;
  1349.         return 0;
  1350.     }
  1351.     ftp->control = tcb;
  1352.     go();
  1353.     return 0;
  1354. }
  1355. /* Parse user FTP commands */
  1356. int
  1357. ftpparse(line,len)
  1358. char *line;
  1359. int16 len;
  1360. {
  1361.     struct mbuf *bp;
  1362.  
  1363.     if(current->cb.ftp->state != COMMAND_STATE){
  1364.         /* The only command allowed in data transfer state is ABORT */
  1365.         if(cmdparse(ftpabort,line) == -1){
  1366.             printf("Transfer in progress; only ABORT is acceptable\r\n");
  1367.         }
  1368.         fflush(stdout);
  1369.         return;
  1370.     }
  1371.  
  1372.     /* Save it now because cmdparse modifies the original */
  1373.     bp = qdata(line,len);
  1374.  
  1375.     if(cmdparse(ftpcmds,line) == -1){
  1376.         /* Send it direct */
  1377.         if(bp != NULLBUF)
  1378.             send_tcp(current->cb.ftp->control,bp);
  1379.         else
  1380.             printf(nospace);
  1381.     } else {
  1382.         free_p(bp);
  1383.     }
  1384.     fflush(stdout);
  1385. }
  1386. /* Translate 'cd' to 'cwd' for convenience */
  1387. static
  1388. int
  1389. docd(argc,argv)
  1390. int argc;
  1391. char *argv[];
  1392. {
  1393.     register struct ftp *ftp;
  1394.  
  1395.     ftp = current->cb.ftp;
  1396.     return sndmsg(ftp,"CWD %s\r\n",argv[1]);
  1397. }
  1398.  
  1399.  
  1400. /* Handle "type" command from user */
  1401. static
  1402. int
  1403. dotype(argc,argv)
  1404. int argc;
  1405. char *argv[];
  1406. {
  1407.     register struct ftp *ftp;
  1408.  
  1409.     ftp = current->cb.ftp;
  1410.     if(argc < 2){
  1411.         switch(ftp->type){
  1412.         case IMAGE_TYPE:
  1413.             printf("Image\r\n");
  1414.             break;
  1415.         case ASCII_TYPE:
  1416.             printf("Ascii\r\n");
  1417.             break;
  1418.         }
  1419.         return 0;
  1420.     }
  1421.     switch(*argv[1]){
  1422.     case 'i':
  1423.     case 'b':
  1424.         ftp->type = IMAGE_TYPE;
  1425.         break;
  1426.     case 'a':
  1427.         ftp->type = ASCII_TYPE;
  1428.         break;
  1429.     default:
  1430.         printf("Invalid type %s\r\n",argv[1]);
  1431.         return 1;
  1432.     }
  1433.     /* Send a TYPE message */
  1434.     return sndmsg(ftp,"TYPE %s\r\n",(ftp->type == ASCII_TYPE) ? "A" : "I");
  1435. }
  1436. /* Start receive transfer. Syntax: get <remote name> [<local name>] */
  1437. static
  1438. doget(argc,argv)
  1439. int argc;
  1440. char *argv[];
  1441. {
  1442.     void r_ftpd(),s_ftp();
  1443.     char *index(),*remotename,*localname;
  1444.     register struct ftp *ftp;
  1445.  
  1446.     ftp = current->cb.ftp;
  1447.     if(ftp == NULLFTP){
  1448.         printf("Not an FTP session!\r\n");
  1449.         return 1;
  1450.     }
  1451.     if(argc < 2){
  1452.         printf("File?\r\n");
  1453.         return 1;
  1454.     }
  1455.     remotename = argv[1];
  1456.     if(argc < 3)
  1457.         localname = remotename;
  1458.     else
  1459.         localname = argv[2];
  1460.  
  1461.     if(ftp->fp != NULLFILE)
  1462.         fclose(ftp->fp);
  1463.  
  1464.     if((ftp->fp = fopen(localname,"w")) == NULLFILE){
  1465.         printf("Cannot write %s\r\n",localname);
  1466.         return 1;
  1467.     }
  1468.     ftp->state = RECEIVING_STATE;
  1469.     ftpsetup(ftp,r_ftpd,NULLVFP,s_ftp);
  1470.  
  1471.     /* Generate the command to start the transfer */
  1472.     return sndmsg(ftp,"RETR %s\r\n",remotename);
  1473. }
  1474. /* List remote directory. Syntax: dir <remote directory/file> [<local name>] */
  1475. static
  1476. dodir(argc,argv)
  1477. int argc;
  1478. char *argv[];
  1479. {
  1480.     void r_ftpd(),s_ftp();
  1481.     char *index(),*localname;
  1482.     register struct ftp *ftp;
  1483.  
  1484.     ftp = current->cb.ftp;
  1485.     if(ftp == NULLFTP){
  1486.         printf("Not an FTP session!\r\n");
  1487.         return 1;
  1488.     }
  1489.     if(argc < 3)
  1490. #ifdef    CPM
  1491.         localname = "con:";
  1492. #endif
  1493. #ifdef    MSDOS
  1494.         localname = "con";
  1495. #endif
  1496. #ifdef    UNIX
  1497.         localname = "/dev/tty";
  1498. #endif
  1499. #ifdef  XENIX
  1500.                 localname = "/dev/tty";
  1501. #endif
  1502. #ifdef  AMIGA
  1503.         localname = "*";
  1504. #endif  AMIGA
  1505.     else
  1506.         localname = argv[2];
  1507.  
  1508.     if(ftp->fp != NULLFILE)
  1509.         fclose(ftp->fp);
  1510.  
  1511.     if((ftp->fp = fopen(localname,"w")) == NULLFILE){
  1512.         printf("Cannot write %s\r\n",localname);
  1513.         return 1;
  1514.     }
  1515.     ftp->state = RECEIVING_STATE;
  1516.     ftpsetup(ftp,r_ftpd,NULLVFP,s_ftp);
  1517.     /* Generate the command to start the transfer
  1518.      * It's done this way to avoid confusing the 4.2 FTP server
  1519.      * if there's no argument
  1520.      */
  1521.     if(argc > 1)
  1522.         return sndmsg(ftp,"LIST %s\r\n",argv[1]);
  1523.     else
  1524.         return sndmsg(ftp,"LIST\r\n","");
  1525. }
  1526. /* Abbreviated (name only) list of remote directory.
  1527.  * Syntax: ls <remote directory/file> [<local name>]
  1528.  */
  1529. static
  1530. dols(argc,argv)
  1531. int argc;
  1532. char *argv[];
  1533. {
  1534.     void r_ftpd(),s_ftp();
  1535.     char *index(),*localname;
  1536.     register struct ftp *ftp;
  1537.  
  1538.     ftp = current->cb.ftp;
  1539.     if(ftp == NULLFTP){
  1540.         printf("Not an FTP session!\r\n");
  1541.         return 1;
  1542.     }
  1543.     if(argc < 3)
  1544. #ifdef    CPM
  1545.         localname = "con:";
  1546. #endif
  1547. #ifdef    MSDOS
  1548.         localname = "con";
  1549. #endif
  1550. #ifdef    UNIX
  1551.         localname = "/dev/tty";
  1552. #endif
  1553. #ifdef    XENIX
  1554.         localname = "/dev/tty";
  1555. #endif
  1556. #ifdef  AMIGA
  1557.         localname = "*";
  1558. #endif  AMIGA
  1559.     else
  1560.         localname = argv[2];
  1561.  
  1562.     if(ftp->fp != NULLFILE)
  1563.         fclose(ftp->fp);
  1564.  
  1565.     if((ftp->fp = fopen(localname,"w")) == NULLFILE){
  1566.         printf("Cannot write %s\r\n",localname);
  1567.         return 1;
  1568.     }
  1569.     ftp->state = RECEIVING_STATE;
  1570.     ftpsetup(ftp,r_ftpd,NULLVFP,s_ftp);
  1571.     /* Generate the command to start the transfer */
  1572.     if(argc > 1)
  1573.         return sndmsg(ftp,"NLST %s\r\n",argv[1]);
  1574.     else
  1575.         return sndmsg(ftp,"NLST\r\n","");
  1576. }
  1577. doone(localname, remotename)
  1578. char *localname, *remotename;
  1579. {
  1580.     void t_ftpd(),s_ftp();
  1581.     struct ftp *ftp;
  1582.  
  1583.     if((ftp = current->cb.ftp) == NULLFTP){
  1584.         printf("Not an FTP session!\r\n");
  1585.         return 1;
  1586.     }
  1587.     if(ftp->fp != NULLFILE)
  1588.         fclose(ftp->fp);
  1589.  
  1590.     if((ftp->fp = fopen(localname,"r")) == NULLFILE){
  1591.         printf("Cannot read %s\r\n",localname);
  1592.         return 1;
  1593.     }
  1594.     ftp->state = SENDING_STATE;
  1595.     ftpsetup(ftp,NULLVFP,t_ftpd,s_ftp);
  1596.  
  1597.     /* Generate the command to start the transfer */
  1598.     return sndmsg(ftp,"STOR %s\r\n",remotename);
  1599. }
  1600. /* Start transmit. Syntax: put <local name> [<remote name>] */
  1601. static
  1602. doput(argc,argv)
  1603. int argc;
  1604. char *argv[];
  1605. {
  1606.  
  1607.     char *remotename,*localname;
  1608.  
  1609.     if(argc < 2){
  1610.         printf("File?\r\n");
  1611.         return 1;
  1612.     }
  1613.     localname = argv[1];
  1614.     if(argc < 3)
  1615.         remotename = localname;
  1616.     else
  1617.         remotename = argv[2];
  1618.     doone(localname, remotename);
  1619. }
  1620.  
  1621. donextmulti()
  1622. {
  1623.  
  1624.   int error;
  1625.     struct ftp *ftp;
  1626.  
  1627.     if((ftp = current->cb.ftp) == NULLFTP){
  1628.         printf("Not an FTP session!\r\n");
  1629.         return 1;
  1630.     }
  1631. again:
  1632.   if (multiargc > 0)
  1633.     {
  1634.       if (dodfind)
  1635.     {
  1636.       error = dfind(&info, multiarg[curmulti], 0);
  1637.       if (error < 0)
  1638.             printf("No files matching %s\n", multiarg[curmulti]);
  1639.       dodfind = 0;
  1640.     }
  1641.        else
  1642.         error = dnext(&info);
  1643.       if (error == 0)
  1644.         doone(info.fib_FileName, info.fib_FileName);
  1645.        else
  1646.         {
  1647.           curmulti++;
  1648.           multiargc--;
  1649.           dodfind = 1;
  1650.       goto again;
  1651.         }
  1652.     }
  1653.   if (multiargc == 0)
  1654.     {
  1655.       printf("Multi put is done\n");
  1656.       ftp->state = COMMAND_STATE;
  1657.     }
  1658. }
  1659. /* Start transmit. Syntax: put <local name> [<remote name>] */
  1660. static
  1661. domput(argc,argv)
  1662. int argc;
  1663. char *argv[];
  1664. {
  1665.     int i;
  1666.     char *remotename,*localname;
  1667.     struct ftp *ftp;
  1668.  
  1669.     if((ftp = current->cb.ftp) == NULLFTP){
  1670.         printf("Not an FTP session!\r\n");
  1671.         return 1;
  1672.     }
  1673.     if(argc < 2){
  1674.         printf("File list?\r\n");
  1675.         return 1;
  1676.     }
  1677.     for(i = 0; i < argc - 1; i++)
  1678.         strncpy(multiarg[i], argv[i+1], MAXFILENAME);
  1679.     multiargc = argc - 1;
  1680.     for(i = 0; i < multiargc; i++)
  1681.         printf("copy %s out\n", multiarg[i]);
  1682.     curmulti = 0;
  1683.     dodfind = 1;
  1684.     donextmulti();
  1685. }
  1686. /* Abort a GET or PUT operation in progress. Note: this will leave
  1687.  * the partial file on the local or remote system
  1688.  */
  1689. doabort(argc,argv)
  1690. int argc;
  1691. char *argv[];
  1692. {
  1693.     register struct ftp *ftp;
  1694.  
  1695.     ftp = current->cb.ftp;
  1696.  
  1697.     /* Close the local file */
  1698.     fclose(ftp->fp);
  1699.     ftp->fp = NULLFILE;
  1700.  
  1701.     switch(ftp->state){
  1702.     case SENDING_STATE:
  1703.         /* Send a premature EOF.
  1704.          * Unfortunately we can't just reset the connection
  1705.          * since the remote side might end up waiting forever
  1706.          * for us to send something.
  1707.          */
  1708.         close_tcp(ftp->data);
  1709.         multiargc = 0;
  1710.         printf("Put aborted\r\n");
  1711.         break;
  1712.     case RECEIVING_STATE:
  1713.         /* Just exterminate the data channel TCB; this will
  1714.          * generate a RST on the next data packet which will
  1715.          * abort the sender
  1716.          */
  1717.         del_tcp(ftp->data);
  1718.         ftp->data = NULLTCB;
  1719.         printf("Get aborted\r\n");
  1720.         break;
  1721.     }
  1722.     ftp->state = COMMAND_STATE;
  1723.     fflush(stdout);
  1724. }
  1725. /* create data port, and send PORT message */
  1726. static
  1727. ftpsetup(ftp,recv,send,state)
  1728. struct ftp *ftp;
  1729. void (*send)();
  1730. void (*recv)();
  1731. void (*state)();
  1732. {
  1733.     struct socket lsocket,fsocket;
  1734.     struct mbuf *bp;
  1735.  
  1736.     lsocket.address = ip_addr;
  1737.     lsocket.port = lport++;
  1738.     fsocket.address = ftp->control->conn.remote.address;
  1739.     fsocket.port = FTPD_PORT;
  1740.  
  1741.     /* Compose and send PORT a,a,a,a,p,p message */
  1742.  
  1743.     if((bp = alloc_mbuf(35)) == NULLBUF){    /* 5 more than worst case */
  1744.         printf(nospace);
  1745.         return;
  1746.     }
  1747.     /* I know, this looks gross, but it works! */
  1748.     sprintf(bp->data,"PORT %u,%u,%u,%u,%u,%u\r\n",
  1749.         hibyte(hiword(lsocket.address)),
  1750.         lobyte(hiword(lsocket.address)),
  1751.         hibyte(loword(lsocket.address)),
  1752.         lobyte(loword(lsocket.address)),
  1753.         hibyte(lsocket.port),
  1754.         lobyte(lsocket.port));
  1755.     bp->cnt = strlen(bp->data);
  1756.     send_tcp(ftp->control,bp);
  1757.  
  1758.     /* Post a listen on the data connection */
  1759.     ftp->data = open_tcp(&lsocket,&fsocket,TCP_PASSIVE,0,
  1760.         recv,send,state,0,(int *)ftp);
  1761.  
  1762. }
  1763. /* FTP control channel receiver upcall routine */
  1764. void
  1765. r_ctl(tcb,cnt)
  1766. register struct tcb *tcb;
  1767. int16 cnt;
  1768. {
  1769.     struct mbuf *bp;
  1770.     struct ftp *ftp;
  1771.  
  1772.     if((ftp = (struct ftp *)tcb->user) == NULLFTP){
  1773.         /* Unknown connection; kill it */
  1774.         close_tcp(tcb);
  1775.         return;
  1776.     }
  1777.     /* Hold output if we're not the current session */
  1778.     if(mode != CONV_MODE || current == NULLSESSION || current->cb.ftp != ftp)
  1779.         return;
  1780.  
  1781.     if(recv_tcp(tcb,&bp,cnt) > 0){
  1782.         while(bp != NULLBUF){
  1783. #ifndef    AMIGA
  1784.             fwrite(bp->data,1,(unsigned)bp->cnt,stdout);
  1785. #else
  1786.             register unsigned len = bp->cnt;
  1787.             register char *dp = bp->data;
  1788.  
  1789.             while (len--)
  1790.                 putchar(*dp++);
  1791. #endif
  1792.             bp = free_mbuf(bp);
  1793.         }
  1794.         fflush(stdout);
  1795.     }
  1796. }
  1797.  
  1798. /* Control channel state change upcall routine */
  1799. static
  1800. void
  1801. s_ctl(tcb,old,new)
  1802. register struct tcb *tcb;
  1803. char old,new;
  1804. {
  1805.     void ftp_delete();
  1806.     struct ftp *ftp;
  1807.     char notify = 0;
  1808.     extern char *tcpstates[];
  1809.     extern char *reasons[];
  1810.     extern char *unreach[];
  1811.     extern char *exceed[];
  1812.  
  1813.     /* Can't add a check for unknown connection here, it would loop
  1814.      * on a close upcall! We're just careful later on.
  1815.      */
  1816.     ftp = (struct ftp *)tcb->user;
  1817.  
  1818.     if(current != NULLSESSION && current->cb.ftp == ftp)
  1819.         notify = 1;
  1820.  
  1821.     switch(new){
  1822.     case CLOSE_WAIT:
  1823.         if(notify)
  1824.             printf("%s\r\n",tcpstates[new]);
  1825.         close_tcp(tcb);
  1826.         break;
  1827.     case CLOSED:    /* heh heh */
  1828.         if(notify){
  1829.             printf("%s (%s",tcpstates[new],reasons[tcb->reason]);
  1830.             if(tcb->reason == NETWORK){
  1831.                 switch(tcb->type){
  1832.                 case DEST_UNREACH:
  1833.                     printf(": %s unreachable",unreach[tcb->code]);
  1834.                     break;
  1835.                 case TIME_EXCEED:
  1836.                     printf(": %s time exceeded",exceed[tcb->code]);
  1837.                     break;
  1838.                 }
  1839.             }
  1840.             printf(")\r\n");
  1841.             cmdmode();
  1842.         }
  1843.         del_tcp(tcb);
  1844.         if(ftp != NULLFTP)
  1845.             ftp_delete(ftp);
  1846.         break;
  1847.     default:
  1848.         if(notify)
  1849.             printf("%s\r\n",tcpstates[new]);
  1850.         break;
  1851.     }
  1852.     if(notify)
  1853.         fflush(stdout);
  1854. }
  1855. /* FTP client data channel connection state change upcall handler */
  1856. static
  1857. void
  1858. s_ftp(tcb,old,new)
  1859. struct tcb *tcb;
  1860. char old,new;
  1861. {
  1862.     struct ftp *ftp;
  1863.  
  1864.     if((ftp = (struct ftp *)tcb->user) == NULLFTP){
  1865.         /* Unknown connection, kill it */
  1866.         close_tcp(tcb);
  1867.         return;
  1868.     }
  1869.     switch(new){
  1870.     case FINWAIT2:
  1871.     case TIME_WAIT:
  1872.         if(ftp != NULLFTP && ftp->state == SENDING_STATE){
  1873.             /* We've received an ack of our FIN, so
  1874.              * return to command mode
  1875.              */
  1876.             if (multiargc <= 0)
  1877.                 {
  1878.                     printf("Multiput done\r\n");
  1879.                     ftp->state = COMMAND_STATE;
  1880.                 }
  1881.             if(current != NULLSESSION && current->cb.ftp == ftp){
  1882.                 printf("Put complete, %lu bytes sent\r\n",
  1883.                     tcb->snd.una - tcb->iss - 2);
  1884.                 fflush(stdout);
  1885.             }
  1886.         }
  1887.         break;        
  1888.     case CLOSE_WAIT:
  1889.         close_tcp(tcb);
  1890.         if(ftp != NULLFTP && ftp->state == RECEIVING_STATE){
  1891.             /* End of file received on incoming file */
  1892. #ifdef    CPM
  1893.             if(ftp->type == ASCII_TYPE)
  1894.                 putc(CTLZ,ftp->fp);
  1895. #endif
  1896.             fclose(ftp->fp);
  1897.             ftp->fp = NULLFILE;
  1898.             ftp->state = COMMAND_STATE;
  1899.             if(current != NULLSESSION && current->cb.ftp == ftp){
  1900.                 printf("Get complete, %lu bytes received\r\n",
  1901.                     tcb->rcv.nxt - tcb->irs - 2);
  1902.                 fflush(stdout);
  1903.             }
  1904.         }
  1905.         break;
  1906.     case CLOSED:
  1907.         if(ftp != NULLFTP)
  1908.             ftp->data = NULLTCB;
  1909.         del_tcp(tcb);
  1910.         if (multiargc > 0)
  1911.           donextmulti();
  1912.         break;
  1913.     }
  1914.  
  1915. }
  1916. /* Send a message on the control channel */
  1917. /*VARARGS*/
  1918. static
  1919. int
  1920. sndmsg(ftp,fmt,arg)
  1921. struct ftp *ftp;
  1922. char *fmt;
  1923. char *arg;
  1924. {
  1925.     struct mbuf *bp;
  1926.     int16 len;
  1927.  
  1928.     len = strlen(fmt) + strlen(arg) + 10;    /* fudge factor */
  1929.     if((bp = alloc_mbuf(len)) == NULLBUF){
  1930.         printf(nospace);
  1931.         return 1;
  1932.     }
  1933.     sprintf(bp->data,fmt,arg);
  1934.     bp->cnt = strlen(bp->data);
  1935.     send_tcp(ftp->control,bp);
  1936.     return 0;
  1937. }
  1938. SHAR_EOF
  1939. cat << \SHAR_EOF > smtpserv.c
  1940. /* SMTP Server state machine - see RFC 821
  1941.  * Very simple implementation; no forwarding allowed
  1942.  * (who wants to re-create "sendmail" ??)
  1943.  */
  1944. #include <stdio.h>
  1945. #include "machdep.h"
  1946. #include "mbuf.h"
  1947. #include "netuser.h"
  1948. #include "timer.h"
  1949. #include "tcp.h"
  1950. #include "smtp.h"
  1951. #ifdef LATTICE
  1952. #define tmpfile(x) tmpnam("testingxxxxxxxxxx")
  1953. #endif
  1954.  
  1955. /* Command table */
  1956. static char *commands[] = {
  1957.     "helo",
  1958. #define    HELO_CMD    0
  1959.     "noop",
  1960. #define    NOOP_CMD    1
  1961.     "mail from:",
  1962. #define    MAIL_CMD    2
  1963.     "quit",
  1964. #define    QUIT_CMD    3
  1965.     "rcpt to:",
  1966. #define    RCPT_CMD    4
  1967.     "help",
  1968. #define    HELP_CMD    5
  1969.     "data",
  1970. #define    DATA_CMD    6
  1971.     "rset",
  1972. #define    RSET_CMD    7
  1973.     NULLCHAR
  1974. };
  1975.  
  1976. /* Reply messages */
  1977. static char help[] = "214-Commands:\r\n214-HELO NOOP MAIL QUIT RCPT HELP DATA RSET\r\n214 End\r\n";
  1978. static char banner[] = "220 %s SMTP Ready\r\n";
  1979. static char closing[] = "221 Closing\r\n";
  1980. static char ok[] = "250 Ok\r\n";
  1981. static char reset[] = "250 Reset state\r\n";
  1982. static char sent[] = "250 Sent\r\n";
  1983. static char ourname[] = "250 %s, Share and Enjoy!\r\n";
  1984. static char enter[] = "354 Enter mail, end with .\r\n";
  1985. static char ioerr[] = "452 Temp file write error\r\n";
  1986. static char mboxerr[] = "452 Mailbox %s write error\r\n";
  1987. static char badcmd[] = "500 Command unrecognized\r\n";
  1988. static char syntax[] = "501 Syntax error\r\n";
  1989. static char needrcpt[] = "503 Need RCPT (recipient)\r\n";
  1990. static char badname[] = "550 Can't open mailbox for %s\r\n";
  1991. static struct tcb *smtp_tcb;
  1992. /* Start up SMTP receiver service */
  1993. smtp_start(argc,argv)
  1994. int argc;
  1995. char *argv[];
  1996. {
  1997.     struct socket lsocket;
  1998.     void r_mail(),s_mail();
  1999.  
  2000.     lsocket.address = ip_addr;
  2001.     if(argc < 2)
  2002.         lsocket.port = SMTP_PORT;
  2003.     else
  2004.         lsocket.port = atoi(argv[1]);
  2005.  
  2006.     smtp_tcb = open_tcp(&lsocket,NULLSOCK,
  2007.         TCP_PASSIVE,0,r_mail,NULLVFP,s_mail,0,(int *)NULL);
  2008. }
  2009.  
  2010. /* Shutdown SMTP service (existing connections are allowed to finish) */
  2011. smtp_stop()
  2012. {
  2013.     if(smtp_tcb != NULLTCB)
  2014.         close_tcp(smtp_tcb);
  2015. }
  2016. /* SMTP connection state change upcall handler */
  2017. static void
  2018. s_mail(tcb,old,new)
  2019. struct tcb *tcb;
  2020. char old,new;
  2021. {
  2022.     struct mail *mp,*mail_create();
  2023.  
  2024.     switch(new){
  2025. #ifdef    QUICKSTART
  2026.     case SYN_RECEIVED:
  2027. #else
  2028.     case ESTABLISHED:
  2029. #endif
  2030.         if((mp = mail_create(tcb)) == NULLMAIL){
  2031.             close_tcp(tcb);
  2032.             break;
  2033.         }
  2034.         tprintf(mp->tcb,banner,hostname);
  2035.         log(tcb,"open SMTP");
  2036.         break;        
  2037.     case CLOSE_WAIT:
  2038.         mp = (struct mail *)tcb->user;
  2039.         mail_delete(mp);                
  2040.         close_tcp(tcb);
  2041.         break;
  2042.     case CLOSED:
  2043.         log(tcb,"close SMTP");
  2044.         del_tcp(tcb);
  2045.         /* Check if server is being shut down */
  2046.         if(tcb == smtp_tcb)
  2047.             smtp_tcb = NULLTCB;
  2048.         break;
  2049.     }
  2050. }
  2051.  
  2052. /* SMTP receiver upcall handler */
  2053. static void
  2054. r_mail(tcb,cnt)
  2055. struct tcb *tcb;
  2056. int16 cnt;
  2057. {
  2058.     register struct mail *mp;
  2059.     char *index(),*inet_ntoa(),c;
  2060.     struct mbuf *bp;
  2061.     void docommand(),deliver(),doline();
  2062.  
  2063.     if((mp = (struct mail *)tcb->user) == NULLMAIL){
  2064.         /* Unknown session */
  2065.         close_tcp(tcb);
  2066.         return;
  2067.     }
  2068.     recv_tcp(tcb,&bp,cnt);
  2069.     /* Assemble an input line in the session buffer.
  2070.      * Return if incomplete
  2071.      */
  2072.     while(pullup(&bp,&c,1) == 1){
  2073.         switch(c){
  2074.         case '\r':    /* Strip cr's */
  2075.             continue;
  2076.         case '\n':    /* Complete line; process it */
  2077.             mp->buf[mp->cnt] = '\0';
  2078.             doline(mp);
  2079.             break;
  2080.         default:    /* Assemble line */
  2081.             mp->buf[mp->cnt++] = c;
  2082.             break;
  2083.         }
  2084.     }
  2085. }
  2086. /* Process a line read on an SMTP connection (any state) */
  2087. static
  2088. void
  2089. doline(mp)
  2090. register struct mail *mp;
  2091. {
  2092.     switch(mp->state){
  2093.     case COMMAND_STATE:
  2094.         docommand(mp);
  2095.         break;
  2096.     case DATA_STATE:
  2097.         tcp_output(mp->tcb);    /* Send ACK; disk I/O is slow */
  2098.         if(mp->buf[0] == '.' && strlen(mp->buf) == 1){
  2099.             fprintf(mp->data,"\n");    /* Leave a blank line between msgs */
  2100.  
  2101.             mp->state = COMMAND_STATE;
  2102.             deliver(mp);    /* Also sends appropriate response */
  2103.             break;
  2104.         }
  2105.         /* Append to data file */
  2106.         if(fprintf(mp->data,"%s\n",mp->buf) < 0){
  2107.             mp->state = COMMAND_STATE;
  2108.             tprintf(mp->tcb,ioerr);
  2109.         }
  2110.         break;
  2111.     }
  2112.     mp->cnt = 0;
  2113. }
  2114. /* Create control block, initialize */
  2115. static struct mail *
  2116. mail_create(tcb)
  2117. register struct tcb *tcb;
  2118. {
  2119.     register struct mail *mp;
  2120.     char *calloc();
  2121.  
  2122.     if((mp = (struct mail *)calloc(1,sizeof (struct mail))) == NULLMAIL){
  2123.         return NULLMAIL;
  2124.     }
  2125.     mp->tcb = tcb;        /* Downward pointer */
  2126.     tcb->user = (int *)mp;    /* Upward pointer */
  2127.     return mp;
  2128. }
  2129.  
  2130. /* Free resources, delete control block */
  2131. static
  2132. mail_delete(mp)
  2133. register struct mail *mp;
  2134. {
  2135.     register struct addr *ap,*ap1;
  2136.  
  2137.     if(mp->system != NULLCHAR)
  2138.         free(mp->system);
  2139.     for(ap = mp->to;ap != NULLADDR;ap = ap1){
  2140.         if(ap->val != NULLCHAR)
  2141.             free(ap->val);
  2142.         ap1 = ap->next;
  2143.         free((char *)ap);
  2144.     }
  2145.     if(mp->data != NULLFILE)
  2146.         fclose(mp->data);
  2147.     free((char *)mp);
  2148. }
  2149.  
  2150. /* Parse and execute mail commands */
  2151. static
  2152. void
  2153. docommand(mp)
  2154. register struct mail *mp;
  2155. {
  2156.     char mailbox[50];
  2157.     char *cmd,*arg,*cp,*cp1,**cmdp;
  2158.     char *index(),*malloc(),*getname();
  2159.     struct tcb *tcb;
  2160.     struct addr *ap;
  2161. #ifdef LATTICE
  2162.     FILE *fp;
  2163. #else
  2164.     FILE *tmpfile(),*fp;
  2165. #endif
  2166. #ifdef    DATE
  2167.     long t;
  2168.     char *ctime();
  2169. #endif
  2170.  
  2171.     cmd = mp->buf;
  2172.     if(mp->cnt < 4){
  2173.         /* Can't be a legal SMTP command */
  2174.         tprintf(mp->tcb,badcmd);
  2175.         return;
  2176.     }    
  2177.     cmd = mp->buf;
  2178.  
  2179.     /* Translate entire buffer to lower case */
  2180.     for(cp = cmd;*cp != '\0';cp++)
  2181.         *cp = tolower(*cp);
  2182.  
  2183.     /* Find command in table; if not present, return syntax error */
  2184.     for(cmdp = commands;*cmdp != NULLCHAR;cmdp++)
  2185.         if(strncmp(*cmdp,cmd,strlen(*cmdp)) == 0)
  2186.             break;
  2187.     if(*cmdp == NULLCHAR){
  2188.         tprintf(mp->tcb,badcmd);
  2189.         return;
  2190.     }
  2191.     arg = &cmd[strlen(*cmdp)];
  2192.     /* Skip spaces after command */
  2193.     while(*arg == ' ')
  2194.         *arg++;
  2195.     /* Execute specific command */
  2196.     switch(cmdp-commands){
  2197.     case HELO_CMD:
  2198.         if((mp->system = malloc((unsigned)strlen(arg)+1)) == NULLCHAR){
  2199.             /* If the system is out of memory, just close */
  2200.             close_tcp(mp->tcb);
  2201.             mail_delete(mp);
  2202.             break;            
  2203.         } else {
  2204.             strcpy(mp->system,arg);
  2205.             tprintf(mp->tcb,ourname,hostname);
  2206.         }
  2207.         break;
  2208.     case NOOP_CMD:
  2209.     case MAIL_CMD:    /* Rather useless */
  2210.         tprintf(mp->tcb,ok);
  2211.         break;
  2212.     case QUIT_CMD:
  2213.         tprintf(mp->tcb,closing);
  2214.         close_tcp(mp->tcb);
  2215.         mail_delete(mp);
  2216.         break;
  2217.     case RCPT_CMD:    /* Specify recipient */
  2218.         if((cp = getname(arg)) == NULLCHAR){
  2219.             tprintf(mp->tcb,syntax);
  2220.             break;
  2221.         }
  2222.         /* Strip the @host part; all names must be local */
  2223.         if((cp1 = index(cp,'@')) != NULLCHAR)
  2224.             *cp1 = '\0';
  2225.  
  2226.         /* Check to see if we can open the mailbox */
  2227.         sprintf(mailbox,MAILSPOOL,cp);
  2228. #ifdef    AMIGA
  2229.         /* probably could use AMIGA's Lock()/Unlock(), but that
  2230.            seems like overkill */
  2231.         if((fp = fopen(mailbox,"r")) == NULL){
  2232. #else
  2233.         if((fp = fopen(mailbox,"a+")) == NULL){
  2234. #endif
  2235.             tprintf(mp->tcb,badname,cp);
  2236.             break;
  2237.         }
  2238.         fclose(fp);
  2239.         /* Allocate an entry on the recipient list. This
  2240.          * assembles the list backwards, but what the heck.
  2241.          */
  2242.         if((ap = (struct addr *)malloc(sizeof(struct addr))) == NULLADDR){
  2243.             close_tcp(mp->tcb);
  2244.             mail_delete(mp);
  2245.             break;
  2246.         }
  2247.         if((ap->val = malloc((unsigned)strlen(mailbox)+1)) == NULLCHAR){
  2248.             free((char *)ap);
  2249.             close_tcp(mp->tcb);
  2250.             mail_delete(mp);
  2251.             break;
  2252.         }
  2253.         strcpy(ap->val,mailbox);
  2254.         ap->next = mp->to;
  2255.         mp->to = ap;
  2256.         tprintf(mp->tcb,ok);
  2257.         break;
  2258.     case HELP_CMD:
  2259.         tprintf(mp->tcb,help);
  2260.         break;
  2261.     case DATA_CMD:
  2262.         if(mp->to == NULLADDR){
  2263.             tprintf(mp->tcb,needrcpt);
  2264.             break;
  2265.         }
  2266.         tcp_output(mp->tcb);    /* Send ACK; disk I/O is slow */
  2267.         if((mp->data = tmpfile()) == NULLFILE){
  2268.             tprintf(mp->tcb,ioerr);
  2269.             break;
  2270.         }
  2271. #ifdef    DATE
  2272.         /* Add timestamp; ctime adds newline */
  2273.         time(&t);
  2274.         if(mp->system != NULLCHAR)
  2275.             fprintf(mp->data,
  2276.                 "Received: from %s by %s\n\twith SMTP ; %s",
  2277.                 mp->system,
  2278.                 hostname,
  2279.                 ctime(&t));
  2280.         else
  2281.             fprintf(mp->data,"Received: %s\n",ctime(&t));
  2282. #endif
  2283.         if(ferror(mp->data)){
  2284.             tprintf(mp->tcb,ioerr);
  2285.         } else {
  2286.             mp->state = DATA_STATE;
  2287.             tprintf(mp->tcb,enter);
  2288.         }
  2289.         break;
  2290.     case RSET_CMD:
  2291.         tcb = mp->tcb;
  2292.         mail_delete(mp);
  2293.         if((mp = mail_create(tcb)) == NULLMAIL){
  2294.             close_tcp(tcb);
  2295.         } else {
  2296.             mp->state = COMMAND_STATE;
  2297.             tprintf(mp->tcb,reset);
  2298.         }
  2299.         break;
  2300.     }
  2301. }
  2302. /* Given a string of the form <user@host>, extract the part inside the
  2303.  * brackets and return a pointer to it.
  2304.  */
  2305. static
  2306. char *
  2307. getname(cp)
  2308. register char *cp;
  2309. {
  2310.     char *cp1;
  2311.     char *index(),*strncpy();
  2312.  
  2313.     if((cp = index(cp,'<')) == NULLCHAR){
  2314.         return NULLCHAR;
  2315.     }
  2316.     cp++;    /* cp -> first char of name */
  2317.     if((cp1 = index(cp,'>')) == NULLCHAR){
  2318.         return NULLCHAR;
  2319.     }
  2320.     *cp1 = '\0';
  2321.     return cp;
  2322. }
  2323. /* Deliver mail to the appropriate mail boxes and delete temp file */
  2324. static
  2325. void
  2326. deliver(mp)
  2327. register struct mail *mp;
  2328. {
  2329.     char *index();
  2330.     int c;
  2331.     register struct addr *ap;
  2332.     register FILE *fp;
  2333.  
  2334.     for(ap = mp->to;ap != NULLADDR;ap = ap->next){
  2335.         fseek(mp->data,0L,0);    /* rewind */
  2336.         fp = fopen(ap->val,"a+");
  2337.         while((c = getc(mp->data)) != EOF){
  2338.             if(putc(c,fp) == EOF)
  2339.                 break;
  2340.         }
  2341.         if(ferror(fp)){
  2342.             fclose(fp);
  2343.             tprintf(mp->tcb,mboxerr,ap->val);
  2344.             fclose(mp->data);
  2345.             mp->data = NULLFILE;
  2346.             return;
  2347.         }
  2348.         fclose(fp);
  2349.     }
  2350.     tprintf(mp->tcb,sent);
  2351.     fclose(mp->data);
  2352.     mp->data = NULLFILE;
  2353. }
  2354. SHAR_EOF
  2355. #    End of shell archive
  2356. exit 0
  2357. -- 
  2358. Bob Page, U of Lowell CS Dept.  page@swan.ulowell.edu  ulowell!page
  2359. Have five nice days.
  2360.